package com.springsource.mvc.InvocableHandler;

import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.method.annotation.ExpressionValueMethodArgumentResolver;
import org.springframework.web.method.annotation.ModelFactory;
import org.springframework.web.method.annotation.RequestHeaderMethodArgumentResolver;
import org.springframework.web.method.annotation.RequestParamMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodArgumentResolverComposite;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.mvc.method.annotation.*;

import java.lang.reflect.Method;
import java.util.List;

public class MyServletInvocableHandlerMethod {

    public static void main(String[] args) throws Exception {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(WebConfig.class);
        RequestMappingHandlerAdapter adapter = new RequestMappingHandlerAdapter();
        adapter.setApplicationContext(context);
        adapter.afterPropertiesSet();




        // 创建请求
        MockHttpServletRequest request  = new MockHttpServletRequest();
        request.setParameter("name", "zhangsan");
        ServletInvocableHandlerMethod handlerMethod =
                new ServletInvocableHandlerMethod(new WebConfig.Controller1(), WebConfig.Controller1.class.getMethod("foo", WebConfig.User.class));
        ServletRequestDataBinderFactory servletRequestDataBinderFactory = new ServletRequestDataBinderFactory(null, null);
        handlerMethod.setDataBinderFactory(servletRequestDataBinderFactory);
        handlerMethod.setParameterNameDiscoverer(new DefaultParameterNameDiscoverer());
        handlerMethod.setHandlerMethodArgumentResolvers(getHandlerMethodArgumentResolverComposite(context));

        ModelAndViewContainer mavContainer = new ModelAndViewContainer();

        // 获取模型工厂 解析ControllerAdvice中方法上的@ModelAttribute
        Method getModelFactory = RequestMappingHandlerAdapter.class.getDeclaredMethod("getModelFactory", HandlerMethod.class, WebDataBinderFactory.class);
        getModelFactory.setAccessible(true);
        ModelFactory modelFactory = (ModelFactory) getModelFactory.invoke(adapter, handlerMethod, servletRequestDataBinderFactory);
        modelFactory.initModel(new ServletWebRequest(request), mavContainer, handlerMethod);

        handlerMethod.invokeAndHandle(new ServletWebRequest(request), mavContainer);
        System.out.println(mavContainer.getModel());
        context.close();
    }


    public static HandlerMethodArgumentResolverComposite getHandlerMethodArgumentResolverComposite(AnnotationConfigApplicationContext context){
        HandlerMethodArgumentResolverComposite argumentResolverComposite = new HandlerMethodArgumentResolverComposite();
        DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();
        argumentResolverComposite.addResolvers(
                // beanFactory可以提供环境变量  false 表示必须有@RequestParam，不允许使用默认值，否则不解析
                new RequestParamMethodArgumentResolver(beanFactory, false),
                new PathVariableMapMethodArgumentResolver(),
                new RequestHeaderMethodArgumentResolver(beanFactory),
                new ServletCookieValueMethodArgumentResolver(beanFactory),
                // 解析@Value、SpEL表达式
                new ExpressionValueMethodArgumentResolver(beanFactory),
                // 解析 req\resp
                new ServletRequestMethodArgumentResolver(),
                // 把Get参数解析成对象，并放入ModelAndViewContainer
                new ServletModelAttributeMethodProcessor(false), // 必须有@ModelAttribute
                new RequestResponseBodyMethodProcessor(List.of(new MappingJackson2HttpMessageConverter())),
                new ServletModelAttributeMethodProcessor(true) // 省略了@ModelAttribute
        );
        return argumentResolverComposite;
    }
}
